﻿using System;
using System.Collections.Generic;

namespace Html
{
    class Scanner
    {
        private string input;
        private int index;
        private Dictionary<char, Kind> evaluator;
        private Token[] buffer;

        public Scanner(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            input = value;
            index = 0;

            evaluator = new Dictionary<char, Kind>()
            {
                { '\u0009', Kind.Whitespace },
                { '\u000A', Kind.Whitespace },
                { '\u000C', Kind.Whitespace },
                { '\u000D', Kind.Whitespace },
                { '\u0020', Kind.Whitespace },
                { '\u0021', Kind.ExclamationMark },
                { '\u0022', Kind.QuotationMarks },
                { '\u0027', Kind.Apostrophe },
                { '\u002F', Kind.Slash },
                { '\u003C', Kind.LessThan },
                { '\u003D', Kind.Equals },
                { '\u003E', Kind.GreaterThan }
            };

            buffer = new Token[1];
            buffer[0] = InternalRead();
        }

        public Token Read()
        {
            Token token = buffer[0];
            buffer[0] = InternalRead();
            return token;
        }

        public Token Peek()
        {
            return buffer[0];
        }

        private Token InternalRead()
        {
            Token token = null;
            if (index < input.Length)
            {
                char c = input[index];
                if (evaluator.ContainsKey(c))
                {
                    Kind kind = evaluator[c];
                    if (kind == Kind.Whitespace)
                    {
                        token = ReadSequence(kind);
                    }
                    else
                    {
                        int length = 1;
                        string value = input.Substring(index, length);
                        index += length;
                        token = new Token(kind, value);
                    }
                }
                else
                {
                    token = ReadSequence(Kind.Text);
                }
            }
            return token;
        }

        private Token ReadSequence(Kind kind)
        {
            int i;
            for (i = index + 1; i < input.Length; i++)
            {
                char c = input[i];
                if (GetKind(c) != kind)
                {
                    break;
                }
            }
            int length = i - index;
            string value = input.Substring(index, length);
            index += length;
            return new Token(kind, value);
        }

        private Kind GetKind(char c)
        {
            Kind kind = Kind.Text;
            if (evaluator.ContainsKey(c))
            {
                kind = evaluator[c];
            }
            return kind;
        }
    }
}
